/*
 * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: MulanPSL-2.0
 */
#![allow(
    dead_code,
    mutable_transmutes,
    non_camel_case_types,
    non_snake_case,
    non_upper_case_globals,
    unused_assignments,
    unused_mut,
    clippy::never_loop
)]
use crate::common::*;
extern "C" {
    fn stat(path:*const libc::c_char,buf:*mut stat) -> libc::c_int;
    fn fstat(filedes:libc::c_int,buf:*mut stat) -> libc::c_int;
    fn snprintf(
        _: *mut libc::c_char,
        _: libc::c_ulong,
        _: *const libc::c_char,
        _: ...
    ) -> libc::c_int;
    fn free(_: *mut libc::c_void);
    fn strcmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
    fn strdup(_: *const libc::c_char) -> *mut libc::c_char;
    fn strrchr(_: *const libc::c_char, _: libc::c_int) -> *mut libc::c_char;
    fn strpbrk(_: *const libc::c_char, _: *const libc::c_char) -> *mut libc::c_char;
    fn strlen(_: *const libc::c_char) -> libc::c_ulong;
    fn lseek(__fd: libc::c_int, __offset: __off_t, __whence: libc::c_int) -> __off_t;
    fn close(__fd: libc::c_int) -> libc::c_int;
    fn read(__fd: libc::c_int, __buf: *mut libc::c_void, __nbytes: size_t) -> ssize_t;
    fn glob(
        __pattern: *const libc::c_char,
        __flags: libc::c_int,
        __errfunc: Option<unsafe extern "C" fn(*const libc::c_char, libc::c_int) -> libc::c_int>,
        __pglob: *mut glob_t,
    ) -> libc::c_int;
    fn globfree(__pglob: *mut glob_t);
    fn opendir(__name: *const libc::c_char) -> *mut DIR;
    fn closedir(__dirp: *mut DIR) -> libc::c_int;
    fn readdir(__dirp: *mut DIR) -> *mut dirent;
    fn fcntl(__fd: libc::c_int, __cmd: libc::c_int, _: ...) -> libc::c_int;
    fn open(__file: *const libc::c_char, __oflag: libc::c_int, _: ...) -> libc::c_int;
    fn __errno_location() -> *mut libc::c_int;
    fn digest_matches(
        fd: libc::c_int,
        file: *const libc::c_char,
        digest: *const command_digest,
    ) -> bool;
    static mut sudo_defs_table: [sudo_defs_types; 0];
    fn sudo_strlcat(dst: *mut libc::c_char, src: *const libc::c_char, siz: size_t) -> size_t;
    static mut sudoers_subsystem_ids: [libc::c_uint; 0];
    static mut sudo_user: sudo_user;
    fn sudo_debug_printf2_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        level: libc::c_int,
        fmt: *const libc::c_char,
        _: ...
    );
    fn sudo_warnx_nodebug_v1(fmt: *const libc::c_char, _: ...);
    fn sudo_strlcpy(dst: *mut libc::c_char, src: *const libc::c_char, siz: size_t) -> size_t;
    fn fnmatch(
        __pattern: *const libc::c_char,
        __name: *const libc::c_char,
        __flags: libc::c_int,
    ) -> libc::c_int;
}

pub const FNM_PATHNAME: libc::c_int = 1 << 0;
pub const O_RDONLY: libc::c_int = 10;
pub const O_NONBLOCK: libc::c_int = 0o4000;
pub const O_EXEC: libc::c_int = 0o10000000;
pub const EACCES: libc::c_int = 13;
pub const PATH_MAX: usize = 4096;
pub const F_GETFD: libc::c_int = 1;
pub const GLOB_NOSORT: libc::c_int = 1 << 2;

pub type __size_t = libc::c_ulong;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct glob_t {
    pub gl_pathc: __size_t,
    pub gl_pathv: *mut *mut libc::c_char,
    pub gl_offs: __size_t,
    pub gl_flags: libc::c_int,
    pub gl_closedir: Option<unsafe extern "C" fn(*mut libc::c_void) -> ()>,
    pub gl_readdir: Option<unsafe extern "C" fn(*mut libc::c_void) -> *mut dirent>,
    pub gl_opendir: Option<unsafe extern "C" fn(*const libc::c_char) -> *mut libc::c_void>,
    pub gl_lstat: Option<unsafe extern "C" fn(*const libc::c_char, *mut stat) -> libc::c_int>,
    pub gl_stat: Option<unsafe extern "C" fn(*const libc::c_char, *mut stat) -> libc::c_int>,
}

#[macro_export]
macro_rules! has_meta {
    ($s:expr) => {
        (!(strpbrk($s, b"\\?*[]\0" as *const u8 as *const libc::c_char)).is_null())
    };
}

fn command_args_match(
    mut sudoers_cmnd: *const libc::c_char,
    mut sudoers_args: *const libc::c_char,
) -> bool {
    let mut flags: libc::c_int = 0 as libc::c_int;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        /*
         * If no args specified in sudoers, any user args are allowed.
         * If the empty string is specified in sudoers, no user args are allowed.
         */
        if sudoers_args.is_null()
            || (sudo_user.cmnd_args).is_null()
                && strcmp(b"\"\"\0" as *const u8 as *const libc::c_char, sudoers_args) == 0
        {
            debug_return_bool!(true);
        }

        /*
         * If args are specified in sudoers, they must match the user args.
         * If running as sudoedit, all args are assumed to be paths.
         */
        if strcmp(
            sudoers_cmnd,
            b"sudoedit\0" as *const u8 as *const libc::c_char,
        ) == 0 as libc::c_int
        {
            flags = FNM_PATHNAME;
        }
        if fnmatch(
            sudoers_args,
            if !(sudo_user.cmnd_args).is_null() {
                sudo_user.cmnd_args as *const libc::c_char
            } else {
                b"\0" as *const u8 as *const libc::c_char
            },
            flags,
        ) == 0 as libc::c_int
        {
            debug_return_bool!(true);
        }

        debug_return_bool!(false);
    } //unsafe
}

/*
 * Stat file by fd is possible, else by path.
 * Returns true on success, else false.
 */
unsafe fn do_stat(mut fd: libc::c_int, mut path: *const libc::c_char, mut sb: *mut stat) -> bool {
    debug_decl!(SUDOERS_DEBUG_MATCH!());

    if fd != -(1 as libc::c_int) {
        debug_return_bool!(fstat(fd, sb) == 0);
    }
    debug_return_bool!(stat(path, sb) == 0);
}

/*
 * Check whether the fd refers to a shell script with a "#!" shebang.
 */
fn is_script(mut fd: libc::c_int) -> bool {
    let mut ret: bool = false;
    let mut magic: [libc::c_char; 2] = [0; 2];
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());
        if read(
            fd,
            magic.as_mut_ptr() as *mut libc::c_void,
            2 as libc::c_int as size_t,
        ) == 2 as libc::c_int as libc::c_long
            && magic[0 as libc::c_int as usize] as libc::c_int == '#' as i32
            && magic[1 as libc::c_int as usize] as libc::c_int == '!' as i32
        {
            ret = true;
        }

        /*if read(
            fd,
            magic.as_mut_ptr() as *mut libc::c_void,
            2 as libc::c_int as size_t,
        ) == 2 as libc::c_int as libc::c_long
        {
            if magic[0 as libc::c_int as usize] as libc::c_int == '#' as i32
                && magic[1 as libc::c_int as usize] as libc::c_int == '!' as i32
            {
                ret = true;
            }
        }*/
        if lseek(fd, 0 as libc::c_int as off_t, 0 as libc::c_int)
            == -(1 as libc::c_int) as libc::c_long
        {
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO | SUDO_DEBUG_LINENO,
                b"unable to rewind script fd\0" as *const u8 as *const libc::c_char
            );
        }

        debug_return_bool!(ret);
    } //unsafe
}

/*
 * Open path if fdexec is enabled or if a digest is present.
 * Returns false on error, else true.
 */
fn open_cmnd(
    mut path: *const libc::c_char,
    mut digest: *const command_digest,
    mut fdp: *mut libc::c_int,
) -> bool {
    let mut fd: libc::c_int = -(1 as libc::c_int);
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        /* Only open the file for fdexec or for digest matching. */
        if def_fdexec!() as libc::c_uint != def_tuple::always as libc::c_uint && digest.is_null() {
            debug_return_bool!(true);
        }

        fd = open(path, O_RDONLY | O_NONBLOCK);
        if fd == -(1 as libc::c_int) && *__errno_location() == EACCES && digest.is_null() {
            /* Try again with O_EXEC if no digest is specified. */
            let saved_errno: libc::c_int = *__errno_location();
            fd = open(path, O_EXEC);
            if fd == -(1 as libc::c_int) {
                *__errno_location() = saved_errno;
            }
        }
        if fd == -(1 as libc::c_int) {
            debug_return_bool!(false);
        }

        fcntl(fd, F_SETFD, FD_CLOEXEC);
        *fdp = fd;
        debug_return_bool!(true);
    } //unsafe
}

fn set_cmnd_fd(mut fd: libc::c_int) {
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());
    }

    unsafe {
        if sudo_user.execfd != -(1 as libc::c_int) {
            close(sudo_user.execfd);
        }

        if fd != -(1 as libc::c_int) {
            if def_fdexec!() as libc::c_uint == def_tuple::never as libc::c_uint {
                /* Never use fexedcve() */
                close(fd);
                fd = -(1 as libc::c_int);
            } else if is_script(fd) {
                let mut fdpath: [libc::c_char; PATH_MAX] = [0; PATH_MAX];
                let mut sb: stat = sb_all_arch;
                let mut flags: libc::c_int = 0;

                /* We can only use fexecve() on a script if /dev/fd/N exists. */
                snprintf(
                    fdpath.as_mut_ptr(),
                    ::core::mem::size_of::<[libc::c_char; PATH_MAX]>() as libc::c_ulong,
                    b"/dev/fd/%d\0" as *const u8 as *const libc::c_char,
                    fd,
                );
                if stat(fdpath.as_mut_ptr(), &mut sb) != 0 as libc::c_int {
                    /* Missing /dev/fd file, can't use fexecve(). */
                    close(fd);
                    fd = -(1 as libc::c_int);
                } else {
                    /*
                     * Shell scripts go through namei twice so we can't have the
                     * close on exec flag set on the fd for fexecve(2).
                     */
                    flags = fcntl(fd, F_GETFD) & !(FD_CLOEXEC);
                    fcntl(fd, F_SETFD, flags);
                }
            }
        }
        sudo_user.execfd = fd;

        debug_return!();
    } //unsafe
}

/*
 * Return true if user_cmnd names one of the inodes in dir, else false.
 */
fn command_matches_dir(
    mut sudoers_dir: *const libc::c_char,
    mut dlen: size_t,
    mut digest: *const command_digest,
) -> bool {
    let mut sudoers_stat: stat = sb_all_arch;
    let mut dent: *mut dirent = std::ptr::null_mut::<dirent>();
    let mut buf: [libc::c_char; PATH_MAX] = [0; PATH_MAX];
    let mut fd: libc::c_int = -(1 as libc::c_int);
    let mut dirp: *mut DIR = std::ptr::null_mut::<DIR>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        /*
         * Grot through directory entries, looking for user_base.
         */
        dirp = opendir(sudoers_dir);
        if dirp.is_null() {
            debug_return_bool!(false);
        }

        if sudo_strlcpy(
            buf.as_mut_ptr(),
            sudoers_dir,
            ::core::mem::size_of::<[libc::c_char; PATH_MAX]>() as libc::c_ulong,
        ) >= ::core::mem::size_of::<[libc::c_char; PATH_MAX]>() as libc::c_ulong
        {
            closedir(dirp);
            debug_return_bool!(false);
        }
        loop {
            dent = readdir(dirp);
            if dent.is_null() {
                break;
            }
            if fd != -(1 as libc::c_int) {
                close(fd);
                fd = -(1 as libc::c_int);
            }

            /* ignore paths > PATH_MAX (XXX - log) */
            buf[dlen as usize] = '\0' as i32 as libc::c_char;
            if sudo_strlcat(
                buf.as_mut_ptr(),
                ((*dent).d_name).as_mut_ptr(),
                ::core::mem::size_of::<[libc::c_char; PATH_MAX]>() as libc::c_ulong,
            ) >= ::core::mem::size_of::<[libc::c_char; PATH_MAX]>() as libc::c_ulong
            {
                continue;
            }

            /* only stat if basenames are the same */
            if strcmp(sudo_user.cmnd_base, ((*dent).d_name).as_mut_ptr()) != 0 as libc::c_int {
                continue;
            }

            /* Open the file for fdexec or for digest matching. */
            if !open_cmnd(buf.as_mut_ptr(), digest, &mut fd) {
                continue;
            }
            if !do_stat(fd, buf.as_mut_ptr(), &mut sudoers_stat) {
                continue;
            }

            if !((sudo_user.cmnd_stat).is_null()
                || (*sudo_user.cmnd_stat).st_dev == sudoers_stat.st_dev
                    && (*sudo_user.cmnd_stat).st_ino == sudoers_stat.st_ino)
            {
                continue;
            }
            if !digest.is_null() && !digest_matches(fd, buf.as_mut_ptr(), digest) {
                continue;
            }
            free(sudo_user.cmnd_safe as *mut libc::c_void);
            sudo_user.cmnd_safe = strdup(buf.as_mut_ptr());
            if (sudo_user.cmnd_safe).is_null() {
                sudo_warnx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
                dent = std::ptr::null_mut::<dirent>();
            }
            break;
        }
        closedir(dirp);

        if !dent.is_null() {
            set_cmnd_fd(fd);
            debug_return_bool!(true);
        }
        if fd != -(1 as libc::c_int) {
            close(fd);
        }
        debug_return_bool!(false);
    } //unsafe
}

fn command_matches_fnmatch(
    mut sudoers_cmnd: *const libc::c_char,
    mut sudoers_args: *const libc::c_char,
    mut digest: *const command_digest,
) -> bool {
    /* XXX - unused */
    let mut sb: stat = sb_all_arch;
    let mut fd: libc::c_int = -(1 as libc::c_int);
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        /*
         * Return true if fnmatch(3) succeeds AND
         *  a) there are no args in sudoers OR
         *  b) there are no args on command line and none required by sudoers OR
         *  c) there are args in sudoers and on command line and they match
         * else return false.
         */
        if fnmatch(sudoers_cmnd, sudo_user.cmnd, FNM_PATHNAME) != 0 as libc::c_int {
            debug_return_bool!(false);
        }
        if command_args_match(sudoers_cmnd, sudoers_args) {
            /* Open the file for fdexec or for digest matching. */
            'bad: loop {
                if !open_cmnd(sudo_user.cmnd, digest, &mut fd) {
                    break 'bad;
                }
                if !do_stat(fd, sudo_user.cmnd, &mut sb) {
                    break 'bad;
                }
                /* Check digest of user_cmnd since sudoers_cmnd is a pattern. */
                if !digest.is_null() && !digest_matches(fd, sudo_user.cmnd, digest) {
                    break 'bad;
                }
                set_cmnd_fd(fd);

                /* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */
                debug_return_bool!(true);
            }
            //bad:
            if fd != -(1 as libc::c_int) {
                close(fd);
                fd = -(1 as libc::c_int);
            }
            debug_return_bool!(false);
        }
        debug_return_bool!(false);
    } //unsafe
}

fn command_matches_glob(
    mut sudoers_cmnd: *const libc::c_char,
    mut sudoers_args: *const libc::c_char,
    mut digest: *const command_digest,
) -> bool {
    let mut sudoers_stat: stat = sb_all_arch;
    let mut bad_digest: bool = false;
    let mut ap: *mut *mut libc::c_char = std::ptr::null_mut::<*mut libc::c_char>();
    let mut base: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut cp: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut fd: libc::c_int = -(1 as libc::c_int);
    let mut dlen: size_t = 0;
    let mut gl: glob_t = glob_t {
        gl_pathc: 0,
        gl_pathv: std::ptr::null_mut::<*mut libc::c_char>(),
        gl_offs: 0,
        gl_flags: 0,
        gl_closedir: None,
        gl_readdir: None,
        gl_opendir: None,
        gl_lstat: None,
        gl_stat: None,
    };
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        /*
         * First check to see if we can avoid the call to glob(3).
         * Short circuit if there are no meta chars in the command itself
         * and user_base and basename(sudoers_cmnd) don't match.
         */
        dlen = strlen(sudoers_cmnd);
        if *sudoers_cmnd.offset(dlen.wrapping_sub(1 as libc::c_int as libc::c_ulong) as isize)
            as libc::c_int
            != '/' as i32
        {
            base = strrchr(sudoers_cmnd, '/' as i32);
            if !base.is_null() {
                base = base.offset(1);
                if !has_meta!(base) && strcmp(sudo_user.cmnd_base, base) != 0 as libc::c_int {
                    debug_return_bool!(false);
                }
            }
        }
        /*
         * Return true if we find a match in the glob(3) results AND
         *  a) there are no args in sudoers OR
         *  b) there are no args on command line and none required by sudoers OR
         *  c) there are args in sudoers and on command line and they match
         * else return false.
         */
        if glob(sudoers_cmnd, GLOB_NOSORT, None, &mut gl) != 0 as libc::c_int
            || gl.gl_pathc == 0 as libc::c_int as libc::c_ulong
        {
            globfree(&mut gl);
            debug_return_bool!(false);
        }
        /* If user_cmnd is fully-qualified, check for an exact match. */
        'done: loop {
            if *(sudo_user.cmnd).offset(0 as libc::c_int as isize) as libc::c_int == '/' as i32 {
                ap = gl.gl_pathv;
                loop {
                    cp = *ap;
                    if cp.is_null() {
                        break;
                    }
                    if fd != -(1 as libc::c_int) {
                        close(fd);
                        fd = -(1 as libc::c_int);
                    }
                    if strcmp(cp, sudo_user.cmnd) == 0 as libc::c_int {
                        /* Open the file for fdexec or for digest matching. */

                        if open_cmnd(cp, digest, &mut fd) && do_stat(fd, cp, &mut sudoers_stat) {
                            if (sudo_user.cmnd_stat).is_null()
                                || (*sudo_user.cmnd_stat).st_dev == sudoers_stat.st_dev
                                    && (*sudo_user.cmnd_stat).st_ino == sudoers_stat.st_ino
                            {
                                /* There could be multiple matches, check digest early. */
                                if !digest.is_null() && !digest_matches(fd, cp, digest) {
                                    bad_digest = true;
                                } else {
                                    free(sudo_user.cmnd_safe as *mut libc::c_void);
                                    sudo_user.cmnd_safe = strdup(cp);
                                    if (sudo_user.cmnd_safe).is_null() {
                                        sudo_warnx!(
                                            b"%s: %s\0" as *const u8 as *const libc::c_char,
                                            get_function_name!(),
                                            b"unable to allocate memory\0" as *const u8
                                                as *const libc::c_char
                                        );
                                        cp = std::ptr::null_mut::<libc::c_char>();
                                        /* fail closed */
                                    }
                                    break;
                                }
                            } else {
                                /* Paths match, but st_dev and st_ino are different. */
                                cp = std::ptr::null_mut::<libc::c_char>();
                                break;
                            }
                        }

                        /*if open_cmnd(cp, digest, &mut fd) {
                            if do_stat(fd, cp, &mut sudoers_stat) {
                                if (sudo_user.cmnd_stat).is_null()
                                    || (*sudo_user.cmnd_stat).st_dev == sudoers_stat.st_dev
                                        && (*sudo_user.cmnd_stat).st_ino == sudoers_stat.st_ino
                                {
                                    /* There could be multiple matches, check digest early. */
                                    if !digest.is_null() && !digest_matches(fd, cp, digest) {
                                        bad_digest = true;
                                    } else {
                                        free(sudo_user.cmnd_safe as *mut libc::c_void);
                                        sudo_user.cmnd_safe = strdup(cp);
                                        if (sudo_user.cmnd_safe).is_null() {
                                            sudo_warnx!(
                                                b"%s: %s\0" as *const u8 as *const libc::c_char,
                                                get_function_name!(),
                                                b"unable to allocate memory\0" as *const u8
                                                    as *const libc::c_char
                                            );
                                            cp = std::ptr::null_mut::<libc::c_char>(); /* fail closed */
                                        }
                                        break;
                                    }
                                } else {
                                    /* Paths match, but st_dev and st_ino are different. */
                                    cp = std::ptr::null_mut::<libc::c_char>();
                                    break;
                                }
                            }
                        }*/
                    }
                    ap = ap.offset(1);
                }
            } else {
                break 'done;
            }
            /* No exact match, compare basename, st_dev and st_ino. */
            if !bad_digest {
                ap = gl.gl_pathv;
                loop {
                    cp = *ap;
                    if cp.is_null() {
                        break;
                    }
                    if fd != -(1 as libc::c_int) {
                        close(fd);
                        fd = -(1 as libc::c_int);
                    }

                    /* If it ends in '/' it is a directory spec. */
                    dlen = strlen(cp);
                    if *cp.offset(dlen.wrapping_sub(1 as libc::c_int as libc::c_ulong) as isize)
                        as libc::c_int
                        == '/' as i32
                    {
                        if command_matches_dir(cp, dlen, digest) {
                            debug_return_bool!(true);
                        }
                    } else {
                        /* Only proceed if user_base and basename(cp) match */
                        base = strrchr(cp, '/' as i32);
                        if !base.is_null() {
                            base = base.offset(1);
                        } else {
                            base = cp;
                        }
                        if strcmp(sudo_user.cmnd_base, base) == 0 as libc::c_int
                            && open_cmnd(cp, digest, &mut fd)
                            && do_stat(fd, cp, &mut sudoers_stat)
                            && ((sudo_user.cmnd_stat).is_null()
                                || (*sudo_user.cmnd_stat).st_dev == sudoers_stat.st_dev
                                    && (*sudo_user.cmnd_stat).st_ino == sudoers_stat.st_ino)
                            && !(!digest.is_null() && !digest_matches(fd, cp, digest))
                        {
                            free(sudo_user.cmnd_safe as *mut libc::c_void);
                            sudo_user.cmnd_safe = strdup(cp);
                            if (sudo_user.cmnd_safe).is_null() {
                                sudo_warnx!(
                                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                                    get_function_name!(),
                                    b"unable to allocate memory\0" as *const u8
                                        as *const libc::c_char
                                );
                                cp = std::ptr::null_mut::<libc::c_char>();
                            }
                            break;
                        }
                    }
                    ap = ap.offset(1);
                } //loop
            } //if
        }
        //done:
        globfree(&mut gl);

        if !cp.is_null() && command_args_match(sudoers_cmnd, sudoers_args) {
            /* safe_cmnd was set above. */
            set_cmnd_fd(fd);
            debug_return_bool!(true);
        }

        /*if !cp.is_null() {
            if command_args_match(sudoers_cmnd, sudoers_args) {
                /* safe_cmnd was set above. */
                set_cmnd_fd(fd);
                debug_return_bool!(true);
            }
        }*/
        if fd != -(1 as libc::c_int) {
            close(fd);
        }
        debug_return_bool!(false);
    } //unsafe
}

fn command_matches_normal(
    mut sudoers_cmnd: *const libc::c_char,
    mut sudoers_args: *const libc::c_char,
    mut digest: *const command_digest,
) -> bool {
    let mut sudoers_stat: stat = sb_all_arch;
    let mut base: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut dlen: size_t = 0;
    let mut fd: libc::c_int = -(1 as libc::c_int);
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        /* If it ends in '/' it is a directory spec. */
        dlen = strlen(sudoers_cmnd);
        if *sudoers_cmnd.offset(dlen.wrapping_sub(1 as libc::c_int as libc::c_ulong) as isize)
            as libc::c_int
            == '/' as i32
        {
            debug_return_bool!(command_matches_dir(sudoers_cmnd, dlen, digest));
        }

        /* Only proceed if user_base and basename(sudoers_cmnd) match */
        base = strrchr(sudoers_cmnd, '/' as i32);
        if base.is_null() {
            base = sudoers_cmnd;
        } else {
            base = base.offset(1);
        }

        if strcmp(sudo_user.cmnd_base, base) != 0 as libc::c_int {
            debug_return_bool!(false);
        }

        /* Open the file for fdexec or for digest matching. */
        'bad: loop {
            if open_cmnd(sudoers_cmnd, digest, &mut fd) {
                break 'bad;
            }

            /*
             * Return true if command matches AND
             *  a) there are no args in sudoers OR
             *  b) there are no args on command line and none req by sudoers OR
             *  c) there are args in sudoers and on command line and they match
             *  d) there is a digest and it matches
             */
            if !(sudo_user.cmnd_stat).is_null()
                && do_stat(fd, sudoers_cmnd, &mut sudoers_stat) as libc::c_int != 0
            {
                if (*sudo_user.cmnd_stat).st_dev != sudoers_stat.st_dev
                    || (*sudo_user.cmnd_stat).st_ino != sudoers_stat.st_ino
                {
                    break 'bad;
                }
            } else {
                /* Either user or sudoers command does not exist, match by name. */
                if strcmp(sudo_user.cmnd, sudoers_cmnd) != 0 as libc::c_int {
                    break 'bad;
                }
            }
            if !command_args_match(sudoers_cmnd, sudoers_args) {
                break 'bad;
            }
            if !digest.is_null() && !digest_matches(fd, sudoers_cmnd, digest) {
                /* XXX - log functions not available but we should log very loudly */
                break 'bad;
            }
            free(sudo_user.cmnd_safe as *mut libc::c_void);
            sudo_user.cmnd_safe = strdup(sudoers_cmnd);
            if (sudo_user.cmnd_safe).is_null() {
                sudo_warnx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
                break 'bad;
            }
            set_cmnd_fd(fd);
            debug_return_bool!(true);
        }
        //bad:
        if fd != -(1 as libc::c_int) {
            close(fd);
        }
        debug_return_bool!(false);
    } //unsafe
}

/*
 * If path doesn't end in /, return true iff cmnd & path name the same inode;
 * otherwise, return true if user_cmnd names one of the inodes in path.
 */
#[no_mangle]
pub fn command_matches(
    mut sudoers_cmnd: *const libc::c_char,
    mut sudoers_args: *const libc::c_char,
    mut digest: *const command_digest,
) -> bool {
    let mut rc: bool = false;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        /* Check for pseudo-commands */
        'done: loop {
            if *sudoers_cmnd.offset(0 as libc::c_int as isize) as libc::c_int != '/' as i32 {
                /*
                 * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
                 *  a) there are no args in sudoers OR
                 *  b) there are no args on command line and none req by sudoers OR
                 *  c) there are args in sudoers and on command line and they match
                 */
                if strcmp(
                    sudoers_cmnd,
                    b"sudoedit\0" as *const u8 as *const libc::c_char,
                ) == 0 as libc::c_int
                    && strcmp(
                        sudo_user.cmnd,
                        b"sudoedit\0" as *const u8 as *const libc::c_char,
                    ) == 0 as libc::c_int
                    && command_args_match(sudoers_cmnd, sudoers_args) as libc::c_int != 0
                {
                    /* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */
                    rc = true;
                }
                break 'done;
            }

            if has_meta!(sudoers_cmnd) {
                /*
                 * If sudoers_cmnd has meta characters in it, we need to
                 * use glob(3) and/or fnmatch(3) to do the matching.
                 */
                if def_fast_glob!() != 0 {
                    rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digest);
                } else {
                    rc = command_matches_glob(sudoers_cmnd, sudoers_args, digest);
                }
            } else {
                rc = command_matches_normal(sudoers_cmnd, sudoers_args, digest);
            }
            break;
        }
        //done:
        sudo_debug_printf!(
            SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
            b"user command \"%s%s%s\" matches sudoers command \"%s%s%s\": %s\0" as *const u8
                as *const libc::c_char,
            sudo_user.cmnd,
            if !(sudo_user.cmnd_args).is_null() {
                b" \0" as *const u8 as *const libc::c_char
            } else {
                b"\0" as *const u8 as *const libc::c_char
            },
            if !(sudo_user.cmnd_args).is_null() {
                sudo_user.cmnd_args as *const libc::c_char
            } else {
                b"\0" as *const u8 as *const libc::c_char
            },
            sudoers_cmnd,
            if !sudoers_args.is_null() {
                b" \0" as *const u8 as *const libc::c_char
            } else {
                b"\0" as *const u8 as *const libc::c_char
            },
            if !sudoers_args.is_null() {
                sudoers_args
            } else {
                b"\0" as *const u8 as *const libc::c_char
            },
            if rc as libc::c_int != 0 {
                b"true\0" as *const u8 as *const libc::c_char
            } else {
                b"false\0" as *const u8 as *const libc::c_char
            }
        );
        debug_return_bool!(rc);
    } //unsafe
}
